/*****************************************************************************************
   
   Copyright (c) Microsoft Corporation. All rights reserved.

   Use of this code sample is subject to the terms of the Microsoft
   Permissive License, a copy of which should always be distributed with
   this file.  You can also access a copy of this license agreement at:
   http://www.microsoft.com/resources/sharedsource/licensingbasics/permissivelicense.mspx

 ****************************************************************************************/

using System;

namespace CommandUtils
{
	public abstract class UtilStatic
	{
		// Format: like String.Format but expanded to include named parameters
		// (where the value is taken from the matching column in the data row).
		// Parameters:
		// - string
		// - DataRow, the row of data to be matched
		// - Variable number of arguments which become the {0}... substitutors, like String.Format
		// It is an error for a field to be specified which does not exist in the data row.
		// Example
		//	CommandUtils.UtilStatic.Format( "The {FIELD_NAME} field in table {0}", myDataRow, strTable );
		//
		public static string Format( string i_Format, System.Data.DataRow i_DataRow )
		{
			FormatHelper formatHelper = new FormatHelper( null, false, i_DataRow, null );
			return int_Format( i_Format, formatHelper );
		}
		public static string Format( string i_Format, System.Data.DataRow i_DataRow, params object[] i_Arguments )
		{
			FormatHelper formatHelper = new FormatHelper( null, false, i_DataRow, i_Arguments );
			return int_Format( i_Format, formatHelper );
		}
		public static string Format( IFormatProvider i_FormatProvider, string i_Format, System.Data.DataRow i_DataRow, params object[] i_Arguments )
		{
			FormatHelper formatHelper = new FormatHelper( i_FormatProvider, false, i_DataRow, i_Arguments );
			return int_Format( i_Format, formatHelper );
		}

		public static string FormatXML( string i_Format, System.Data.DataRow i_DataRow )
		{
			FormatHelper formatHelper = new FormatHelper( null, true, i_DataRow, null );
			return int_Format( i_Format, formatHelper );
		}
		public static string FormatXML( string i_Format, System.Data.DataRow i_DataRow, params object[] i_Arguments )
		{
			FormatHelper formatHelper = new FormatHelper( null, true, i_DataRow, i_Arguments );
			return int_Format( i_Format, formatHelper );
		}
		public static string FormatXML( IFormatProvider i_FormatProvider, string i_Format, System.Data.DataRow i_DataRow, params object[] i_Arguments )
		{
			FormatHelper formatHelper = new FormatHelper( i_FormatProvider, true, i_DataRow, i_Arguments );
			return int_Format( i_Format, formatHelper );
		}

		// Same, with IDictionary instead of DataRow.
		// Dictionary keys and values must be string (or we'll coerce).
		// It is an error for a field to be specified which does not exist in the dictionary.
		public static string Format( string i_Format, System.Collections.IDictionary i_Dictionary )
		{
			FormatHelper formatHelper = new FormatHelper( null, false, i_Dictionary, null );
			return int_Format( i_Format, formatHelper );
		}
		public static string Format( string i_Format, System.Collections.IDictionary i_Dictionary, params object[] i_Arguments )
		{
			FormatHelper formatHelper = new FormatHelper( null, false, i_Dictionary, i_Arguments );
			return int_Format( i_Format, formatHelper );
		}
		public static string Format( IFormatProvider i_FormatProvider, string i_Format, System.Collections.IDictionary i_Dictionary, params object[] i_Arguments )
		{
			FormatHelper formatHelper = new FormatHelper( i_FormatProvider, false, i_Dictionary, i_Arguments );
			return int_Format( i_Format, formatHelper );
		}

		public static string FormatXML( string i_Format, System.Collections.IDictionary i_Dictionary )
		{
			FormatHelper formatHelper = new FormatHelper( null, true, i_Dictionary, null );
			return int_Format( i_Format, formatHelper );
		}
		public static string FormatXML( string i_Format, System.Collections.IDictionary i_Dictionary, params object[] i_Arguments )
		{
			FormatHelper formatHelper = new FormatHelper( null, true, i_Dictionary, i_Arguments );
			return int_Format( i_Format, formatHelper );
		}
		public static string FormatXML( IFormatProvider i_FormatProvider, string i_Format, System.Collections.IDictionary i_Dictionary, params object[] i_Arguments )
		{
			FormatHelper formatHelper = new FormatHelper( i_FormatProvider, true, i_Dictionary, i_Arguments );
			return int_Format( i_Format, formatHelper );
		}

		private static string int_Format( string i_Format, FormatHelper i_Helper )
		{
			string ret;
			string regex = @"\{[^\{\}]*\}";
			if( i_Format==null ) i_Format="";
			System.Text.RegularExpressions.MatchEvaluator MatchEvaluator = i_Helper.GetEvaluator();
			ret = System.Text.RegularExpressions.Regex.Replace( i_Format, regex, MatchEvaluator );
			return ret;
		}


		public static string XMLEncode(string i_String)
		{
			if (i_String != "")
			{
				i_String = i_String.Replace("<", "&lt;");
				i_String = i_String.Replace(">", "&gt;");
				i_String = i_String.Replace("&", "&amp;");
				i_String = i_String.Replace("\"", "&quot;");
				i_String = i_String.Replace("'", "&apos;");
			}

			return i_String;
		}
	}

	class FormatHelper
	{
		private IFormatProvider m_FormatProvider;
		private bool m_EncodeXML;
		private System.Data.DataRow m_DataRow;
		private System.Collections.IDictionary m_Dictionary;
		private object[] m_Arguments;
		private bool m_PassUnrecognizedTokens = true;

		public FormatHelper( IFormatProvider i_FormatProvider, bool i_EncodeXML, System.Data.DataRow i_DataRow, params object[] i_Arguments )
		{
			m_FormatProvider = i_FormatProvider;
			m_EncodeXML = i_EncodeXML;
			m_DataRow = i_DataRow;
			m_Dictionary = null;
			m_Arguments = i_Arguments;
		}

		public FormatHelper( IFormatProvider i_FormatProvider, bool i_EncodeXML, System.Collections.IDictionary i_Dictionary, params object[] i_Arguments )
		{
			m_FormatProvider = i_FormatProvider;
			m_EncodeXML = i_EncodeXML;
			m_DataRow = null;
			m_Dictionary = i_Dictionary;
			m_Arguments = i_Arguments;
		}

		public System.Text.RegularExpressions.MatchEvaluator GetEvaluator()
		{
			return new System.Text.RegularExpressions.MatchEvaluator( this.EvalMatch );
		}

		public string EvalMatch( System.Text.RegularExpressions.Match i_Match )
		{
			// If the match value is the name of a field in the data row,
			// replace with the value of the field from the data row.
			// Otherwise use the string.Format replacer.

			string FieldName = i_Match.Value;
			// Strip the curly braces from each end
			FieldName = FieldName.Substring( 1, FieldName.Length - 2 );
			if( FieldName.Length == 0 )
			{
				throw new FormatException( "Illegal formatting replacer: " + i_Match.Value  );
			}

			// If the match value (contents of the curly braces) doesn't begin with a number,
			// we're expecting it to be a field-name or a hashtable-key.
			string Value = null;
			if( "0123456789".IndexOf( FieldName[0] )==-1 )
			{
				// Convert the i_Match.Value (eg: "{thisfield,5}") to a regular format-string eg: "{0,5}"
				// (the fieldnmame ends at the first contain comma or colon; for examples of formatting see
				// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconFormattingOverview.asp)
				char[] aSep = { ',', ':' };
				int iSep = FieldName.IndexOfAny( aSep );
				string FormatStr = "{0}";
				if( iSep >= 0 )
				{
					FormatStr = "{0" + FieldName.Substring(iSep) + "}";
					FieldName = FieldName.Substring(0, iSep);
				}
				if( m_DataRow != null )
				{
					if( m_DataRow[ FieldName ]!=null )
					{
						if( m_FormatProvider==null )
						{
							Value = String.Format( FormatStr, m_DataRow[ FieldName ] );
						}
						else
						{
							Value = String.Format( m_FormatProvider, FormatStr, m_DataRow[ FieldName ] );
						}
					}
					else if( m_PassUnrecognizedTokens )
					{
						Value = i_Match.Value;
					}
					else
					{
						throw new FormatException( "Key not in record: " + FieldName );
					}
				}
				else if( m_Dictionary != null )
				{
					if( m_Dictionary.Contains( FieldName ) )
					{
						if( m_FormatProvider==null )
						{
							Value = String.Format( FormatStr, m_Dictionary[ FieldName ] );
						}
						else
						{
							Value = String.Format( m_FormatProvider, FormatStr, m_Dictionary[ FieldName ] );
						}
					}
					else if( m_PassUnrecognizedTokens )
					{
						Value = i_Match.Value;
					}
					else
					{
						throw new FormatException( "Key not in dictionary: " + FieldName );
					}
				}
				else if( m_PassUnrecognizedTokens )
				{
					Value = i_Match.Value;
				}
				else
				{
					throw new FormatException( "Key not known: " + FieldName );
				}
			}
			else
			{
				// Use the regular string formatter.
				// (This can throw, of course)
				if( m_FormatProvider==null )
				{
					Value = String.Format( i_Match.Value, m_Arguments );
				}
				else
				{
					Value = String.Format( m_FormatProvider, i_Match.Value, m_Arguments );
				}
			}

			if( m_EncodeXML )
			{
				// The replacement value needs to be XML-entity-encoded
				// for example, where we're inserting data from a database record into an existing XML string
				Value = UtilStatic.XMLEncode( Value );
			}

			return Value;
		}
	}
}
